#pragma once
#include "Sock.h"
#include "Theadlib/thread.h"
#include "snetwork/NluConnection.h"

#define NL_HTTP_MAX_URI_CHANGES           10
#define NL_HTTP_MAX_SUBREQUESTS           50

/* must be 2^n */
#define NL_HTTP_LC_HEADER_LEN             32


#define NL_HTTP_DISCARD_BUFFER_SIZE       4096
#define NL_HTTP_LINGERING_BUFFER_SIZE     4096


#define NL_HTTP_VERSION_9                 9
#define NL_HTTP_VERSION_10                1000
#define NL_HTTP_VERSION_11                1001

#define NL_HTTP_UNKNOWN                   0x0001
#define NL_HTTP_GET                       0x0002
#define NL_HTTP_HEAD                      0x0004
#define NL_HTTP_POST                      0x0008
#define NL_HTTP_PUT                       0x0010
#define NL_HTTP_DELETE                    0x0020
#define NL_HTTP_MKCOL                     0x0040
#define NL_HTTP_COPY                      0x0080
#define NL_HTTP_MOVE                      0x0100
#define NL_HTTP_OPTIONS                   0x0200
#define NL_HTTP_PROPFIND                  0x0400
#define NL_HTTP_PROPPATCH                 0x0800
#define NL_HTTP_LOCK                      0x1000
#define NL_HTTP_UNLOCK                    0x2000
#define NL_HTTP_PATCH                     0x4000
#define NL_HTTP_TRACE                     0x8000

#define NL_HTTP_CONNECTION_CLOSE          1
#define NL_HTTP_CONNECTION_KEEP_ALIVE     2


#define NL_HTTP_NONE                           1


#define NL_HTTP_PARSE_HEADER_DONE         1

#define NL_HTTP_CLIENT_ERROR              10
#define NL_HTTP_PARSE_INVALID_METHOD      10
#define NL_HTTP_PARSE_INVALID_REQUEST     11
#define NL_HTTP_PARSE_INVALID_09_METHOD   12

#define NL_HTTP_PARSE_INVALID_HEADER      13


/* unused                                  1 */
#define NL_HTTP_SUBREQUEST_IN_MEMORY      2
#define NL_HTTP_SUBREQUEST_WAITED         4
#define NL_HTTP_LOG_UNSAFE                8


#define NL_HTTP_OK                        200
#define NL_HTTP_CREATED                   201
#define NL_HTTP_ACCEPTED                  202
#define NL_HTTP_NO_CONTENT                204
#define NL_HTTP_PARTIAL_CONTENT           206

#define NL_HTTP_SPECIAL_RESPONSE          300
#define NL_HTTP_MOVED_PERMANENTLY         301
#define NL_HTTP_MOVED_TEMPORARILY         302
#define NL_HTTP_SEE_OTHER                 303
#define NL_HTTP_NOT_MODIFIED              304

#define NL_HTTP_BAD_REQUEST               400
#define NL_HTTP_UNAUTHORIZED              401
#define NL_HTTP_FORBIDDEN                 403
#define NL_HTTP_NOT_FOUND                 404
#define NL_HTTP_NOT_ALLOWED               405
#define NL_HTTP_REQUEST_TIME_OUT          408
#define NL_HTTP_CONFLICT                  409
#define NL_HTTP_LENGTH_REQUIRED           411
#define NL_HTTP_PRECONDITION_FAILED       412
#define NL_HTTP_REQUEST_ENTITY_TOO_LARGE  413
#define NL_HTTP_REQUEST_URI_TOO_LARGE     414
#define NL_HTTP_UNSUPPORTED_MEDIA_TYPE    415
#define NL_HTTP_RANGE_NOT_SATISFIABLE     416


/* Our own HTTP codes */

/* The special code to close connection without any response */
#define NL_HTTP_CLOSE                     444

#define NL_HTTP_NGINX_CODES               494

#define NL_HTTP_REQUEST_HEADER_TOO_LARGE  494

#define NL_HTTP_HTTPS_CERT_ERROR               495
#define NL_HTTP_HTTPS_NO_CERT                  496

/*
 * We use the special code for the plain HTTP requests that are sent to
 * HTTPS port to distinguish it from 4XX in an error page redirection
 */
#define NL_HTTP_TO_HTTPS                  497

/* 498 is the canceled code for the requests with invalid host name */

/*
 * HTTP does not define the code for the case when a client closed
 * the connection while we are processing its request so we introduce
 * own code to log such situation when a client has closed the connection
 * before we even try to send the HTTP header to it
 */
#define NL_HTTP_CLIENT_CLOSED_REQUEST     499


#define NL_HTTP_INTERNAL_SERVER_ERROR     500
#define NL_HTTP_NOT_IMPLEMENTED           501
#define NL_HTTP_BAD_GATEWAY               502
#define NL_HTTP_SERVICE_UNAVAILABLE       503
#define NL_HTTP_GATEWAY_TIME_OUT          504
#define NL_HTTP_INSUFFICIENT_STORAGE      507


#define NL_HTTP_LOWLEVEL_BUFFERED         0xf0
#define NL_HTTP_WRITE_BUFFERED            0x10
#define NL_HTTP_GZIP_BUFFERED             0x20
#define NL_HTTP_SSI_BUFFERED              0x01
#define NL_HTTP_SUB_BUFFERED              0x02
#define NL_HTTP_COPY_BUFFERED             0x04

typedef struct http_chain_s http_chain_t;

typedef struct {
    int                         fd;
    const char *                name;
    struct stat                info;
    off_t                      offset;
    off_t                      sys_offset;
    unsigned                   valid_info:1;
    unsigned                   directio:1;
} http_file_t;

typedef struct {
    char           *pos;
    char           *last;
    char           *start;         /* start of buffer */
    char           *end;           /* end of buffer */
    
    http_file_t      *file;
    off_t            file_pos;
    off_t            file_last;

    /* the buf's content could be changed */
    unsigned         temporary:1;

    /*
     * the buf's content is in a memory cache or in a read only memory
     * and must not be changed
     */
    unsigned         memory:1;

    /* the buf's content is mmap()ed and must not be changed */
    unsigned         mmap:1;

    unsigned         recycled:1;
    unsigned         in_file:1;
    unsigned         flush:1;
    unsigned         sync:1;
    unsigned         last_buf:1;
    unsigned         last_in_chain:1;

    unsigned         last_shadow:1;
    unsigned         temp_file:1;
} http_buf_t;

struct http_chain_s {
    http_buf_t    *buf;
    http_chain_t  *next;
};

typedef struct {
    //ngx_list_t                        headers;

    char                        *host;
    char                        *connection;
    char                        *if_modified_since;
    char                        *if_unmodified_since;
    char                        *user_agent;
    char                        *referer;
    char                        *content_length;
    char                        *content_type;

    char                        *range;
    char                        *if_range;

    char                        *transfer_encoding;
    char                        *expect;

    char                        *accept_encoding;
    char                        *via;

    char                        *authorization;

    char                            *keep_alive;

    char                            *user;
    char                            *passwd;

    //ngx_array_t                       cookies;

    char*                             server;
    off_t                             content_length_n;
    time_t                            keep_alive_n;

    unsigned                          connection_type:2;
    unsigned                          msie:1;
    unsigned                          msie4:1;
    unsigned                          msie6:1;
    unsigned                          opera:1;
    unsigned                          gecko:1;
    unsigned                          chrome:1;
    unsigned                          safari:1;
    unsigned                          konqueror:1;
} nl_http_headers_in_t;


typedef struct {
    //ngx_list_t                        headers;

    unsigned int                        status;
    char                                *status_line;

    char                                *server;
    char                                *date;
    char                                *content_length;
    char                                *content_encoding;
    char                                *location;
    char                                *refresh;
    char                                *last_modified;
    char                                *content_range;
    char                                *accept_ranges;
    char                                *www_authenticate;
    char                                *expires;
    char                                *etag;

    char                                *override_charset;

    size_t                              content_type_len;
    char                                *content_type;
    char                                *charset;
    char                                *content_type_lowcase;
    unsigned int                        content_type_hash;

    //ngx_array_t                       cache_control;

    off_t                               content_length_n;
    time_t                              date_time;
    time_t                              last_modified_time;
} nl_http_headers_out_t;

typedef struct {
    unsigned int                        signature;         /* "HTTP" */
    nl_connection_t                     *connection;
    time_t                              lingering_time;
    time_t                              start_sec;
    unsigned int                        start_msec;
    unsigned int                        method;    
    size_t                              limit_rate;
    char*                               request_line;
    char*                               uri;
    char*                               args;
    char*                               exten;
    char*                               unparsed_uri;

    char*                               method_name;
    char*                               http_protocol;
    unsigned int                        http_version;
    
    http_chain_t                        *out;
    http_chain_t                        *out_head;    
    http_buf_t                          *header_in;
    http_buf_t                          *header_out;
    nl_http_headers_in_t                *headers_in;
    nl_http_headers_out_t               *headers_out;
    
    /* used to learn the Apache compatible response length without a header */
    size_t                              header_size;
    off_t                               request_length;
    unsigned int                        err_status;
    unsigned                            count:8;
    unsigned                            NL_HTTP_state:4;
    unsigned                            waited:1;    
    unsigned                            subrequests:8;
    unsigned                            blocked:8;
    unsigned                            aio:1;

    unsigned                            http_state:4;

    /* URI with "/." and on Win32 with "//" */
    unsigned                            complex_uri:1;

    /* URI with "%" */
    unsigned                            quoted_uri:1;

    /* URI with "+" */
    unsigned                            plus_in_uri:1;

    /* URI with " " */
    unsigned                            space_in_uri:1;
    unsigned                            invalid_header:1;
    unsigned                            add_uri_to_alias:1;
    unsigned                            valid_location:1;
    unsigned                            valid_unparsed_uri:1;
    unsigned                            uri_changed:1;
    unsigned                            uri_changes:4;
    unsigned                            request_body_in_single_buf:1;
    unsigned                            request_body_in_file_only:1;
    unsigned                            request_body_in_persistent_file:1;
    unsigned                            request_body_in_clean_file:1;
    unsigned                            request_body_file_group_access:1;
    unsigned                            request_body_file_log_level:3;
    unsigned                            subrequest_in_memory:1;

    /* used to parse HTTP headers */

    unsigned int                        state;
    char*                               upstream;
    
    unsigned int                        header_hash;
    unsigned int                        lowcase_index;
    char                                lowcase_header[NL_HTTP_LC_HEADER_LEN];
    char                                *header_name_start;
    char                                *header_name_end;
    char                                *header_start;
    char                                *header_end;
    char                                *uri_start;
    char                                *uri_end;
    char                                *uri_ext;
    char                                *args_start;
    char                                *request_start;
    char                                *request_end;
    char                                *method_end;
    char                                *schema_start;
    char                                *schema_end;
    char                                *host_start;
    char                                *host_end;
    char                                *port_start;
    char                                *port_end;
    
    unsigned                            http_minor:16;
    unsigned                            http_major:16;

} nlu_http_request_t;

typedef nlu_http_request_t nl_http_request_t;

namespace Sock
{
    class  SNETWORK_API CNluHttpRequest
    {
    public:
        static CNluHttpRequest *instance();
        int execute(nlu_event_t *rev);
        int _write_file_tmp(std::string& action, std::string& filename);
        int RecvStaticData(paramArr *par, char *buf, long int iSize);
        ssize_t UnixRecv(nl_connection_t *c, char *buf, size_t size);
        void HttpProcessRequestLine(nl_http_request_t *r);
        void HttpProcessRequestHeaders(nl_http_request_t *r, http_buf_t *b);     
        ssize_t HttpReadRequestHeader(nl_http_request_t *r);       
        int SetNonBlocking(int sockfd);
    protected:
	    ssize_t m_cnt_len;
	    paramArr *m_par;
	    int m_sockfd;
	
    private: 
        char m_method[30];
        static CNluHttpRequest* m_instance;
        CNluHttpRequest();    
        class CGarbo {public: ~CGarbo(){if (CNluHttpRequest::m_instance) delete CNluHttpRequest::m_instance;}};
        static CGarbo Garbo;	
    };
}
